/**
 * ETSDB 数据库测试文件
 * 
 * 本文件包含对ETSDB数据库核心功能的测试
 */

// 导入ETSDB模块
import etsdb from '../src/etsdb.js';
import { ObjectId } from '../src/Utils.js';

// 测试辅助函数
function assertEquals(actual, expected, message = '') {
  if (actual !== expected) {
    throw new Error(`${message || '断言失败'}: 期望 ${expected}，实际 ${actual}`);
  }
  console.log(`✓ ${message || '测试通过'}`);
}

function assertDeepEquals(actual, expected, message = '') {
  const actualStr = JSON.stringify(actual);
  const expectedStr = JSON.stringify(expected);
  
  if (actualStr !== expectedStr) {
    throw new Error(`${message || '断言失败'}: \n期望 ${expectedStr}\n实际 ${actualStr}`);
  }
  console.log(`✓ ${message || '测试通过'}`);
}

function assertThrows(fn, errorMessage = '', message = '') {
  let thrown = false;
  try {
    fn();
  } catch (error) {
    if (errorMessage && !error.message.includes(errorMessage)) {
      throw new Error(`${message || '断言失败'}: 期望错误信息包含 "${errorMessage}"，实际为 "${error.message}"`);
    }
    thrown = true;
  }
  
  if (!thrown) {
    throw new Error(`${message || '断言失败'}: 期望抛出异常，但未抛出`);
  }
  console.log(`✓ ${message || '测试通过'}`);
}

/**
 * 数据库测试套件
 */
async function runDatabaseTests() {
  console.log('====== ETSDB 数据库测试 ======');
  
  // 测试数据库连接
  await testDatabaseConnection();
  
  // 测试模型定义
  await testModelDefinition();
  
  // 测试CRUD操作
  await testCrudOperations();
  
  // 测试事务处理
  await testTransactions();
  
  // 测试ObjectId生成
  testObjectId();
  
  // 测试查询操作
  await testQueries();
  
  // 测试变更监听
  await testChangeListeners();
  
  // 测试配置选项
  await testConfiguration();
  
  console.log('\n====== ETSDB 数据库测试全部通过 ======');
}

/**
 * 测试数据库连接
 */
async function testDatabaseConnection() {
  console.log('\n测试数据库连接:');
  
  try {
    // 测试默认连接
    const db1 = etsdb.openDatabase('test-db-1');
    assertEquals(typeof db1, 'object', '数据库连接返回对象');
    assertEquals(db1.constructor.name, 'Database', '返回Database实例');
    
    // 测试配置连接
    const db2 = etsdb.openDatabase({
      name: 'test-db-2',
      inMemory: true
    });
    assertEquals(typeof db2, 'object', '带配置的数据库连接返回对象');
    
    // 测试关闭连接
    await db1.close();
    await db2.close();
    console.log('✓ 数据库关闭测试通过');
  } catch (error) {
    console.error('数据库连接测试失败:', error);
    throw error;
  }
}

/**
 * 测试模型定义
 */
async function testModelDefinition() {
  console.log('\n测试模型定义:');
  
  try {
    const db = etsdb.openDatabase({
      name: 'model-test-db',
      inMemory: true
    });
    
    // 测试基本模型定义
    const User = db.model('User', {
      name: String,
      age: Number,
      active: Boolean,
      createdAt: Date
    });
    assertEquals(typeof User, 'object', '模型定义返回对象');
    assertEquals(User.modelName, 'User', '模型名称正确');
    
    // 测试模型验证
    assertThrows(() => {
      db.model('InvalidModel', {
        invalidField: 'invalidType' // 无效的类型
      });
    }, 'Invalid type', '无效类型的模型定义抛出异常');
    
    await db.close();
  } catch (error) {
    console.error('模型定义测试失败:', error);
    throw error;
  }
}

/**
 * 测试CRUD操作
 */
async function testCrudOperations() {
  console.log('\n测试CRUD操作:');
  
  try {
    const db = etsdb.openDatabase({
      name: 'crud-test-db',
      inMemory: true
    });
    
    const Product = db.model('Product', {
      name: String,
      price: Number,
      stock: Number,
      category: String
    });
    
    // 测试创建
    const product1 = await Product.create({
      name: '测试产品1',
      price: 99.99,
      stock: 100,
      category: '电子产品'
    });
    assertEquals(typeof product1._id, 'string', '创建的文档包含_id字段');
    assertEquals(product1.name, '测试产品1', '创建的文档数据正确');
    
    // 测试查询所有
    const allProducts = await Product.find();
    assertEquals(allProducts.length, 1, '查询所有文档返回正确数量');
    
    // 测试条件查询
    const电子产品 = await Product.find({ category: '电子产品' });
    assertEquals(电子产品.length, 1, '条件查询返回正确数量');
    
    // 测试根据ID查询
    const foundProduct = await Product.findById(product1._id);
    assertEquals(foundProduct._id, product1._id, '根据ID查询返回正确文档');
    
    // 测试更新
    const updatedProduct = await Product.updateById(product1._id, {
      $set: { price: 109.99 },
      $inc: { stock: -10 }
    });
    assertEquals(updatedProduct.price, 109.99, '更新后的价格正确');
    assertEquals(updatedProduct.stock, 90, '更新后的库存正确');
    
    // 测试删除
    const deleted = await Product.deleteById(product1._id);
    assertEquals(deleted._id, product1._id, '删除的文档ID正确');
    
    const remainingProducts = await Product.find();
    assertEquals(remainingProducts.length, 0, '删除后文档数量为0');
    
    await db.close();
  } catch (error) {
    console.error('CRUD操作测试失败:', error);
    throw error;
  }
}

/**
 * 测试事务处理
 */
async function testTransactions() {
  console.log('\n测试事务处理:');
  
  try {
    const db = etsdb.openDatabase({
      name: 'transaction-test-db',
      inMemory: true
    });
    
    const Account = db.model('Account', {
      name: String,
      balance: Number
    });
    
    // 测试成功的事务
    await db.beginTransaction();
    try {
      await Account.create({ name: '账户1', balance: 1000 });
      await Account.create({ name: '账户2', balance: 500 });
      await db.commitTransaction();
      
      const accounts = await Account.find();
      assertEquals(accounts.length, 2, '事务提交后文档数量正确');
    } catch (error) {
      await db.rollbackTransaction();
      throw error;
    }
    
    // 测试失败的事务（回滚）
    await db.beginTransaction();
    try {
      await Account.update({ name: '账户1' }, { $set: { balance: 2000 } });
      // 故意抛出错误以测试回滚
      throw new Error('测试事务回滚');
      await db.commitTransaction();
    } catch (error) {
      await db.rollbackTransaction();
      
      // 检查数据是否回滚
      const account1 = await Account.find({ name: '账户1' });
      assertEquals(account1[0].balance, 1000, '事务回滚后数据恢复');
    }
    
    await db.close();
  } catch (error) {
    console.error('事务处理测试失败:', error);
    throw error;
  }
}

/**
 * 测试ObjectId生成
 */
function testObjectId() {
  console.log('\n测试ObjectId生成:');
  
  try {
    // 测试生成新的ObjectId
    const id1 = new ObjectId();
    const id2 = new ObjectId();
    
    assertEquals(typeof id1.toString(), 'string', 'ObjectId转换为字符串');
    assertEquals(id1.toString().length, 24, 'ObjectId长度为24个字符');
    assertEquals(id1.toString() !== id2.toString(), true, '连续生成的ObjectId不相同');
    
    // 测试验证ObjectId
    assertEquals(ObjectId.isValid(id1.toString()), true, '有效的ObjectId验证通过');
    assertEquals(ObjectId.isValid('invalid-id'), false, '无效的ObjectId验证失败');
    
    // 测试从现有ID创建
    const id3 = new ObjectId(id1.toString());
    assertEquals(id3.toString(), id1.toString(), '从现有ID创建的ObjectId相同');
    
    // 测试无效ID创建
    assertThrows(() => new ObjectId('invalid-id'), 'Invalid ObjectId', '无效ID创建抛出异常');
    
  } catch (error) {
    console.error('ObjectId测试失败:', error);
    throw error;
  }
}

/**
 * 测试查询操作
 */
async function testQueries() {
  console.log('\n测试查询操作:');
  
  try {
    const db = etsdb.openDatabase({
      name: 'query-test-db',
      inMemory: true
    });
    
    const Student = db.model('Student', {
      name: String,
      age: Number,
      score: Number,
      grade: String
    });
    
    // 插入测试数据
    await Student.create({
      name: '学生1',
      age: 20,
      score: 85,
      grade: 'A'
    });
    await Student.create({
      name: '学生2',
      age: 22,
      score: 92,
      grade: 'A'
    });
    await Student.create({
      name: '学生3',
      age: 21,
      score: 76,
      grade: 'B'
    });
    
    // 测试等于查询
    const gradeAStudents = await Student.find({ grade: 'A' });
    assertEquals(gradeAStudents.length, 2, '等于查询返回正确数量');
    
    // 测试比较查询
    const olderStudents = await Student.find({ age: { $gt: 20 } });
    assertEquals(olderStudents.length, 2, '大于查询返回正确数量');
    
    const highScoreStudents = await Student.find({ score: { $gte: 80 } });
    assertEquals(highScoreStudents.length, 2, '大于等于查询返回正确数量');
    
    // 测试复合查询
    const aStudentsWithHighScore = await Student.find({
      grade: 'A',
      score: { $gt: 90 }
    });
    assertEquals(aStudentsWithHighScore.length, 1, '复合查询返回正确数量');
    
    await db.close();
  } catch (error) {
    console.error('查询操作测试失败:', error);
    throw error;
  }
}

/**
 * 测试变更监听
 */
async function testChangeListeners() {
  console.log('\n测试变更监听:');
  
  try {
    const db = etsdb.openDatabase({
      name: 'listener-test-db',
      inMemory: true
    });
    
    const Task = db.model('Task', {
      title: String,
      completed: Boolean
    });
    
    // 测试监听
    let changeCount = 0;
    let lastChange = null;
    
    const listener = (change) => {
      changeCount++;
      lastChange = change;
    };
    
    db.watch('Task', listener);
    
    // 创建文档触发监听
    const task = await Task.create({
      title: '测试任务',
      completed: false
    });
    
    // 等待监听触发
    await new Promise(resolve => setTimeout(resolve, 100));
    
    assertEquals(changeCount, 1, '监听触发次数正确');
    assertEquals(lastChange.type, 'save', '变更类型正确');
    
    // 取消监听
    db.unwatch('Task', listener);
    
    // 再次操作不应该触发监听
    await Task.updateById(task._id, {
      $set: { completed: true }
    });
    
    await new Promise(resolve => setTimeout(resolve, 100));
    assertEquals(changeCount, 1, '取消监听后不再触发');
    
    await db.close();
  } catch (error) {
    console.error('变更监听测试失败:', error);
    throw error;
  }
}

/**
 * 测试配置选项
 */
async function testConfiguration() {
  console.log('\n测试配置选项:');
  
  try {
    // 测试内存数据库
    const inMemoryDb = etsdb.openDatabase({
      name: 'in-memory-db',
      inMemory: true
    });
    
    const stats = await inMemoryDb.getStats();
    assertEquals(stats.type, 'memory', '内存数据库类型正确');
    
    // 测试调试模式
    const debugDb = etsdb.openDatabase({
      name: 'debug-db',
      debug: true
    });
    assertEquals(debugDb.config.debug, true, '调试模式配置正确');
    
    await inMemoryDb.close();
    await debugDb.close();
  } catch (error) {
    console.error('配置选项测试失败:', error);
    throw error;
  }
}

// 运行测试
runDatabaseTests().catch(error => {
  console.error('测试失败:', error);
});